home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / MiscKit1.7.1 / MiscKit / Palettes / MiscArrowButtonPalette / MiscArrowButton.subproj / MiscArrowButtonCell.m < prev    next >
Encoding:
Text File  |  1995-04-12  |  9.8 KB  |  400 lines

  1. // Copyright (C) 1995 Robert Todd Thomas
  2. // Use is governed by the MiscKit license
  3.  
  4. /******************************************************************
  5.  * CLASS:            MiscArrowButtonCell
  6.  *
  7.  *    See the header for more information on this class.
  8.  *
  9.  * This object is included in the MiscKit by permission from the author
  10.  * and its use is governed by the MiscKit license, found in the file
  11.  * "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
  12.  * for a list of all applicable permissions and restrictions.
  13.  *******************************************************************/
  14.  
  15. #import "wraps.h"
  16. #import <dpsclient/psops.h>
  17. #import <apps/InterfaceBuilder.h>
  18. #import "MiscArrowButtonCell.h"
  19.  
  20. // These #defines are used for versioning instances of this class, so that
  21. // you will always be able to read any previously archived version. If you
  22. // make changes to the class's ivars, bump the version number up one and 
  23. // make the appropriate changes to the read: method.
  24.  
  25. #define MISC_ABC_VERSION 1
  26. #define MISC_ABC_CLASSNAME "MiscArrowButtonCell"
  27.  
  28.  
  29. @implementation MiscArrowButtonCell
  30.  
  31. + initialize
  32. {
  33.     if (self == [MiscArrowButtonCell class])
  34.         [self setVersion: MISC_ABC_VERSION];
  35.     
  36.     return self;
  37. }
  38.  
  39.  
  40.  
  41. // The initializing methods just call the designated initializer
  42. // and give some default values.
  43.  
  44. - init
  45. {
  46.     return [self initTextCell: "Left" altTitle: "Right"];
  47. }
  48.  
  49.  
  50.  
  51. - initTextCell: (const char *)aString
  52. {
  53.     return [self initTextCell: aString altTitle: "Right"];
  54. }
  55.  
  56.  
  57.  
  58. // The desinated initializer for this class.
  59.  
  60. - initTextCell: (const char *)aString altTitle: (const char *)altString
  61. {
  62.     [super initTextCell: aString];
  63.     
  64.     [self setAltTitle: altString];
  65.     [self setState: 0];
  66.     [self setBordered: NO];
  67.     [self setShowsStateBy: NX_NONE];
  68.     [self setHighlightsBy: NX_NONE];
  69.     [self setArrowAlignment: MISC_ARROW_ABSOLUTE];
  70.     [self setType: NX_TOGGLE];
  71.     
  72.     return self;
  73. }
  74.  
  75.  
  76.  
  77. // Since the stringValue in Button does not do much, it now
  78. // returns the currently selected text.
  79.  
  80. - (const char *)stringValue
  81. {
  82.     if ([self state] == 0)
  83.         return (const char *)[self title];
  84.     else
  85.         return (const char *)[self altTitle];
  86. }
  87.  
  88.  
  89.  
  90. // I believe this method is supposed to calculate the minimum size needed to 
  91. // fit the currently displayed contents  (contents, altContents, and the arrow)
  92. // in the cellframe.
  93.  
  94. - calcCellSize: (NXSize *)theSize inRect: (NXRect *)theRect
  95. {
  96.   float  contentsWidth = [ [self font] getWidthOf: [self title] ];
  97.   float  altContentsWidth = [ [self font] getWidthOf: [self altTitle] ];
  98.   
  99.     // first calculate the width needed to draw all text and the arrow
  100.       
  101.       theSize->width = 0.0;
  102.     if ([self arrowAlignment] == MISC_ARROW_RELATIVE)
  103.     {
  104.         theSize->width += contentsWidth;
  105.         theSize->width += altContentsWidth;
  106.      }
  107.     else    // absolute alignment
  108.     {
  109.         if (contentsWidth > altContentsWidth)
  110.             theSize->width += contentsWidth * 2.0;
  111.         else
  112.             theSize->width += altContentsWidth * 2.0;
  113.      }
  114.      
  115.     theSize->width += cellHeight + 10.0;                    
  116.     
  117.     // now the height (which will usually be cellHeight)
  118.     
  119.     theSize->height = cellHeight;
  120.     
  121.     if ([ [self font] pointSize] > cellHeight)
  122.         theSize->height = [ [self font] pointSize];
  123.              
  124.     return self;
  125. }
  126.  
  127.  
  128.  
  129. // As far as I can tell (which may not be all that far) is that this 
  130. // function is only used by the Button's IBEditor to tell how large
  131. // the editor should be.
  132.  
  133. - getTitleRect : (NXRect *)theRect
  134. {
  135.   float  size = [ [self font] pointSize];
  136.   float  theY;
  137.   float  maxWidth = theRect->size.width/2.0;
  138.                 
  139.     // this is a hack so you can double click on the altTitle in IB
  140.  
  141.     if ([self state])
  142.         return [self getAltTitleRect: theRect];
  143.  
  144.     [self setAlignment: NX_LEFTALIGNED];    // used for the IBEditor
  145.         
  146.     theY = theRect->origin.y + theRect->size.height/2.0 - size/2.0;
  147.     
  148.     if ([self arrowAlignment] == MISC_ARROW_RELATIVE)
  149.           maxWidth = [ [self font] getWidthOf: [self title] ] + 10.0;      
  150.         
  151.     NXSetRect (theRect, theRect->origin.x, theY-2.0, maxWidth, size+2.0);               
  152.  
  153.     return self;
  154. }
  155.  
  156.  
  157.  
  158. // If the state is 1 (altContents selected), then this method will be
  159. // called. It returns the location for editor to appear when editing 
  160. // the altContents.
  161.  
  162. - getAltTitleRect: (NXRect *)theRect
  163. {
  164.   float  size = [ [self font] pointSize];
  165.   float  newX, newY, newWidth;
  166.     
  167.     [self setAlignment: NX_RIGHTALIGNED];    // used for the IBEditor
  168.  
  169.     newX = theRect->origin.x + theRect->size.width/2.0;              
  170.     newY = theRect->origin.y + theRect->size.height/2.0 - size/2.0;
  171.     newWidth = theRect->size.width/2.0;
  172.     
  173.     NXSetRect (theRect, newX, newY-2.0, newWidth, size+2.0);               
  174.     
  175.     return self;
  176. }
  177.  
  178.  
  179.  
  180. // New method to set the alignment of the arrow. MISC_ARROW_ABSOLUTE aligns
  181. // the arrow in the center of the cellFrame, where MISC_ARROW_RELATIVE
  182. // centers the arrow between the contents and altContents.
  183.  
  184. - setArrowAlignment: (int)alignment
  185. {
  186.     arrowAlignment = alignment;
  187.     return self;
  188. }
  189.  
  190.  
  191.  
  192. - (int)arrowAlignment
  193. {
  194.     return arrowAlignment;
  195. }
  196.  
  197.  
  198.  
  199. // This method is used to draw only the parts of the cell that
  200. // do change when the state changes. Therefore, only the black arrow
  201. // and the text are drawn here.
  202.  
  203. - drawInside: (const NXRect *)cellFrame inView: controlView
  204. {
  205.   float  gray;
  206.   float  size = [ [self font] pointSize];
  207.             
  208.     // draw the arrow
  209.  
  210.     if ([self arrowAlignment] == MISC_ARROW_RELATIVE)
  211.     {
  212.       NXRect  relativeFrame;
  213.       float  contentsWidth = [ [self font] getWidthOf: [self title] ];
  214.         float  altContentsWidth = [ [self font] getWidthOf: [self altTitle] ];
  215.  
  216.         // since it the arrow is drawn relative to the text, a little
  217.         // calculation is needed
  218.         
  219.         relativeFrame.origin.x = cellFrame->origin.x + contentsWidth;
  220.         relativeFrame.size.width = cellFrame->size.width - 
  221.                 (contentsWidth + altContentsWidth);
  222.  
  223.         PSABdrawarrow (relativeFrame.origin.x, cellFrame->origin.y + 3.0, 
  224.                 relativeFrame.size.width, cellFrame->size.height - 6.0, 
  225.                 (int)[self state], [self isEnabled]);
  226.      }
  227.     else
  228.         // draw the arrow in the center of the cellFrame
  229.         
  230.         PSABdrawarrow (cellFrame->origin.x, cellFrame->origin.y + 3.0, 
  231.                 cellFrame->size.width, cellFrame->size.height - 6.0, 
  232.                 (int)[self state], [self isEnabled]);
  233.     
  234.     // draw the left and right hand text
  235.     
  236.     [ [self font] set];
  237.             
  238.     if ([self title] != NULL)
  239.     {    
  240.       float  theY;
  241.                   
  242.         if ([self state] || ![self isEnabled])
  243.             gray = NX_DKGRAY;
  244.         else 
  245.             gray = 0.0;
  246.  
  247.         // calculate the placement of the contents and print it
  248.         
  249.         theY = cellFrame->origin.y + cellFrame->size.height/2.0 + size/3.0;            
  250.  
  251.         PSABshowstring (cellFrame->origin.x + 3.0, theY, gray, [self title]);
  252.      }
  253.          
  254.     if ([self altTitle] != NULL)
  255.     {    
  256.       float  theX, theY;
  257.       float  strWidth = [ [self font] getWidthOf: [self altTitle] ];
  258.                   
  259.         if ([self state] && [self isEnabled])
  260.             gray = 0.0;
  261.         else
  262.             gray = NX_DKGRAY;
  263.         
  264.         // calculate the placement of the altContents and print it
  265.         
  266.         theX = cellFrame->origin.x+(cellFrame->size.width - strWidth - 3.0);
  267.         theY = cellFrame->origin.y + cellFrame->size.height/2.0 + size/3.0;
  268.                     
  269.         PSABshowstring (theX, theY, gray, [self altTitle]);             
  270.      }
  271.              
  272.     return self;
  273. }
  274.  
  275.  
  276.  
  277. // This part of the drawing displays only the parts that don't change
  278. // often, which includes the diamond that encloses the arrow, and the
  279. // border when I get around to adding one. 
  280.  
  281. - drawSelf: (const NXRect *)cellFrame inView: controlView
  282. {          
  283.     // if transparent draw the background white (when in IB) and 
  284.     // same as the background when in an app (or testing interface)
  285.     
  286.     if ([self isTransparent])
  287.     {    
  288.         if ([NXApp respondsTo: @selector(isTestingInterface)])
  289.             if ([NXApp isTestingInterface])
  290.                 PSsetgray (NX_LTGRAY);
  291.             else
  292.                 PSsetgray (1.0);
  293.         else
  294.             PSsetgray (NX_LTGRAY);
  295.         
  296.         NXRectFill (cellFrame);
  297.  
  298.         return self;
  299.      }
  300.     
  301.     // set cellHeight since some of the calculations that are in other
  302.     // methods have to know the height of the cell (this is probably the
  303.     // wrong way to go about this)
  304.     
  305.     cellHeight = cellFrame->size.height;
  306.     
  307.     // draw the border or bezel, then call drawInside
  308.     
  309.     if ([self isBordered])
  310.         NXDrawButton (cellFrame, cellFrame);
  311.         
  312.     PSgsave();
  313.  
  314.     // have to flip drawing if we are drawing into a flipped view
  315.             
  316.     if ([controlView isFlipped])
  317.         PSABflipme (cellFrame->size.height + (cellFrame->origin.y * 2.0));
  318.     
  319.     if ([self arrowAlignment] == MISC_ARROW_RELATIVE)
  320.     {
  321.       NXRect  relativeFrame;
  322.       float  contentsWidth = [ [self font] getWidthOf: [self title] ];
  323.         float  altContentsWidth = [ [self font] getWidthOf: [self altTitle] ];
  324.  
  325.         relativeFrame.origin.x = cellFrame->origin.x + contentsWidth;
  326.         relativeFrame.size.width = cellFrame->size.width - 
  327.                 (contentsWidth + altContentsWidth);
  328.         
  329.         PSABdrawdiamond (relativeFrame.origin.x, cellFrame->origin.y + 3.0,
  330.                 relativeFrame.size.width, cellFrame->size.height - 6.0);
  331.      }
  332.     else
  333.         PSABdrawdiamond (cellFrame->origin.x, cellFrame->origin.y + 3.0, 
  334.             cellFrame->size.width, cellFrame->size.height - 6.0);
  335.         
  336.     PSgrestore();                
  337.  
  338.     // draw the rest of the cell
  339.                 
  340.     [self drawInside: cellFrame inView: controlView];
  341.      
  342.     return self;
  343. }
  344.  
  345.  
  346.  
  347. // Archiving methods
  348.  
  349. - read: (NXTypedStream *)stream
  350. {
  351.     int  version;
  352.     
  353.     [super read: stream];
  354.     version = NXTypedStreamClassVersion (stream, MISC_ABC_CLASSNAME);
  355.     
  356.     switch (version)
  357.     {
  358.       case 0:
  359.           // First version had no ivars, but set any new ivars to a useable
  360.         // value.
  361.           arrowAlignment = MISC_ARROW_ABSOLUTE;    
  362.           break;
  363.       case 1:
  364.           // Current version.
  365.         NXReadType (stream, @encode(unsigned int), &arrowAlignment);
  366.         break;
  367.       default:
  368.           break;
  369.      }
  370.  
  371.     return self;
  372. }
  373.  
  374.  
  375.  
  376. - write: (NXTypedStream *)stream
  377. {
  378.     [super write: stream];
  379.     
  380.     // You can also keep versioning here just in case you would like 
  381.     // to back to an older version, but I don't.
  382.     NXWriteType (stream, @encode(unsigned int), &arrowAlignment);
  383.     return self;
  384. }
  385.  
  386. @end
  387.  
  388.  
  389. /******************************************************************
  390.   CHANGES:
  391.       1. Now use setArrowAlignment/arrowAlignment to set/get whether the
  392.       the arrow is relative or absolutely aligned. Added arrowAlignment 
  393.      instance var.
  394.    October 1, 1994:
  395.     2. Added archive versioning (+initialize).
  396.     
  397.  *******************************************************************/
  398.  
  399.  
  400.